import React, { useState, useEffect, ReactNode } from "react";
import { Upload, Button, message as Message, message, Form } from 'antd'
import { UploadOutlined, PlusOutlined, InboxOutlined } from '@ant-design/icons'

import { normFile, getFileList } from '@/utils/util'
import api from '../../utils/api'

import type { FormItemProps } from 'antd/es/form'
import type { RcFile, UploadFile, UploadProps, UploadChangeParam } from 'antd/es/upload/interface'

interface ImgMeasure {
  width?: number
  height?: number
  maxWidth?: number
  maxHeight?: number
}

export interface IUploadProps extends UploadProps {
  dragger?: boolean
  extraTip?: string
  maxSize?: number | string
  buttonName?: string
  fileType?: 'excel' | 'image' | 'pdf'
  fileUrlList?: string[]
  urlPath?: string
  measure?: ImgMeasure
  onBeforeUpload?: (file: RcFile) => boolean | Promise<boolean>
  onUploadSuccess?: (obj: { code?: number } | null) => void
}

const { Dragger } = Upload
const defaultAction = api.commonUploadImage

const getMaxSize = (size: string | number) => {
  if (typeof size === 'number') {
    return size * 1024 * 1024
  } else {
    const matchs = /(\d+)(\w+)/.exec(size)
    const [input, fileSize, measure]: any = matchs
    if (measure.toLowerCase().indexOf('kb') > -1) {
      return fileSize * 1024
    } else if (measure.toLowerCase().indexOf('mb') > -1) {
      return fileSize * 1024 * 1024
    }
  }
  return size as any
}

const checkMeasure = (measure: ImgMeasure, file: RcFile) => {
  return new Promise(resolve => {
    const fileReader = new FileReader()
    fileReader.addEventListener('load', function () {
      const image = new Image()
      image.onload = function () {
        if (measure.width && image.width !== measure.width) {
          message.error(`请上传宽度为${measure.width}的图片`)
          resolve(false)
        } else if (measure.height && image.height !== measure.height) {
          message.error(`请上传高度为${measure.height}的图片`)
          resolve(false)
        } else if (measure.maxWidth && image.width > measure.maxWidth) {
          message.error(`请上传宽度不大于${measure.maxWidth}的图片`)
          resolve(false)
        } else if (measure.maxHeight && image.height > measure.maxHeight) {
          message.error(`请上传高度不大于${measure.maxHeight}的图片`)
          resolve(false)
        } else {
          resolve(true)
        }
      }
      image.src = fileReader.result as any
    })
    fileReader.readAsDataURL(file)
  })
}

const IUpload: React.FC<IUploadProps> = props => {
  const {
    action,
    extraTip,
    onUploadSuccess,
    buttonName,
    data = {},
    maxSize = 0,
    maxCount = 1,
    dragger = false,
    multiple = false,
    disabled = false,
    fileType = 'image',
    listType = 'picture',
    urlPath = 'presignedUrl',
    fileUrlList = [],
  } = props

  const [fileList, setFileList] = useState<UploadFile[]>(props.fileList || [])

  useEffect(() => {
    if (Array.isArray(fileUrlList) && fileUrlList?.length) {
      setInitFileList()
    }
  }, [fileUrlList])

  const setInitFileList = () => {
    const initFileList = getFileList(fileUrlList)
    const fileList = initFileList.map(rawFile => {
      return {
        ...rawFile,
        response: { code: 0, data: rawFile.url },
      }
    })
    const info = {
      file: fileList[fileList.length - 1],
      fileList: fileList,
    }
    setFileList(initFileList)
    props.onChange && props.onChange(info)
  }

  const beforeUpload = async (file: RcFile) => {
    if (fileList.length >= maxCount) {
      Message.warning(`最多上传${maxCount}个文件！`)
      return Upload.LIST_IGNORE
    }

    const isExcel = /\.(xls|xlsx)$/.test(file.name)
    const isImg = /\.(png|jpg|jpeg)$/.test(file.name)
    const isPdf = /\.(pdf)$/.test(file.name)

    if (fileType === 'image' && !isImg) {
      Message.error('请上传.png/.jpg/.jpeg格式的图片！')
      return Upload.LIST_IGNORE
    }
    if (fileType === 'excel' && !isExcel) {
      Message.error('请上传excel！')
      return Upload.LIST_IGNORE
    }
    if (fileType === 'pdf' && !isPdf) {
      Message.error('请上传pdf！')
      return Upload.LIST_IGNORE
    }
    if (props.onBeforeUpload) {
      const result = await props.onBeforeUpload(file)
      if (!result) {
        return Upload.LIST_IGNORE
      }
    }
    // 验证宽高尺寸
    if (isImg && props.measure) {
      const result = await checkMeasure(props.measure, file)
      if (!result) {
        return Upload.LIST_IGNORE
      }
    }
    // 验证大小
    if (file.size && maxSize && file.size > getMaxSize(maxSize)) {
      const tip = typeof maxSize === 'string' ? maxSize : maxSize + 'MB'
      Message.warning(`文件体积不得超过${tip}！`)
      return Upload.LIST_IGNORE
    }
    return isExcel || isImg || isPdf
  }

  const handleChange: UploadProps['onChange'] = (info: UploadChangeParam<UploadFile>) => {
    const file = info.file || {}
    const { status, name: fileName } = file
    if (status === 'done' && file.response) {
      if (file.response.code === 0) {
        if (/http(s)?:\/\//.test(file.response.data?.[urlPath])) {
          Object.assign(file, file.response.data)
          file.url = file.response.data?.[urlPath]
        } else if (/http(s)?:\/\//.test(file.response.data)) {
          file.url = file.response.data
        }
        onUploadSuccess?.({ ...file.response })
        Message.success(file.response.message || `${fileName}上传成功！`)
      } else {
        file.status = 'error'
        Message.error(file.response.message || '上传失败，请重试！')
      }
    } else if (status === 'error') {
      onUploadSuccess?.(null)
      Message.error(`${fileName}上传失败！`)
    }
    if (!info.fileList?.length) {
      onUploadSuccess?.(null)
    }
    props.onChange && props.onChange(info)
    setFileList([...info.fileList])
  }

  const uploadProps: UploadProps = {
    name: 'file',
    data: data,
    listType: listType,
    maxCount: maxCount,
    action: action || defaultAction,
    multiple: multiple,
    beforeUpload: beforeUpload,
    onChange: handleChange,
  }

  return dragger ? (
    <Dragger {...uploadProps} fileList={fileList} disabled={disabled}>
      <p className="ant-upload-drag-icon">
        <InboxOutlined />
      </p>
      <p className="ant-upload-text">点击或将文件拖到该区域上传</p>
    </Dragger>
  ) : (
    <Upload {...uploadProps} fileList={fileList} disabled={disabled}>
      {listType === 'picture-card' ? (
        <div>
          <PlusOutlined />
          <div style={{ marginTop: 8 }}>Upload</div>
        </div>
      ) : (
        <Button disabled={disabled} icon={<UploadOutlined />}>
          {buttonName || fileType === 'image' ? '上传图片' : '上传文件'}
        </Button>
      )}
      {extraTip && <span className="ml5">{extraTip}</span>}
    </Upload>
  )
}

interface ParsedProps {
  showParsed: boolean
  resultTextMap?: Record<string, '解析成功' | '解析失败' | '重复' | '待上传'>
}

export interface IUploadItemProps {
  formItemProps?: FormItemProps
  childProps?: IUploadProps
  parsedProps?: ParsedProps
}

const getTip = (maxSize: IUploadProps['maxSize'], measure?: ImgMeasure) => {
  let tip = typeof maxSize === 'string' ? maxSize : maxSize + 'MB'
  if (measure) {
    if (measure.width || measure.height) {
      if (measure.width && measure.height) {
        tip = tip + `，宽高为${measure.width}x${measure.height}的图片`
      } else if (measure.width) {
        tip = tip + `，宽为${measure.width}的图片`
      } else if (measure.height) {
        tip = tip + `，高为${measure.height}的图片`
      }
    } else if (measure.maxWidth || measure.maxHeight) {
      if (measure.maxWidth && measure.maxHeight) {
        tip = tip + `，最大宽高为${measure.maxWidth}x${measure.maxHeight}的图片`
      } else if (measure.maxWidth) {
        tip = tip + `，最大宽为${measure.maxWidth}的图片`
      } else if (measure.maxHeight) {
        tip = tip + `，最大高为${measure.maxHeight}的图片`
      }
    }
  }
  return tip
}

const renderParseResult = (parsedProps?: ParsedProps, parsedResult?: Record<string, ReactNode>) => {
  const { resultTextMap } = parsedProps || {}
  const result = resultTextMap
    ? resultTextMap
    : {
        successCount: '解析成功',
        errorCount: '解析失败',
        repeatCount: '重复',
        toUploadCount: '待上传',
      }
  return (
    <div className="startStart">
      {Object.keys(result).map(key => (
        <div key={key} className="columnCenterStart mr20">
          <span>{result[key as keyof typeof result]}</span>
          <span>{parsedResult?.[key as keyof typeof parsedResult]}</span>
        </div>
      ))}
    </div>
  )
}

const IUploadItem: React.FC<IUploadItemProps> = props => {
  const { formItemProps = {}, childProps = {}, parsedProps } = props
  const { fileType = 'image', maxCount, maxSize, measure } = childProps
  const [parsedResult, setParsedResult] = useState(null)

  const handleUploadSuccess = (result: any) => {
    props.childProps?.onUploadSuccess?.(result)
    if (fileType === 'excel' && parsedProps?.showParsed) {
      setParsedResult({
        successCount: '解析成功',
        errorCount: '解析失败',
        repeatCount: '重复',
        toUploadCount: '待上传',
      } as any)
    }
  }

  const tip = getTip(maxSize, measure)
  return (
    <>
      <Form.Item
        valuePropName="fileList"
        getValueFromEvent={normFile}
        extra={
          formItemProps.extra
            ? formItemProps.extra
            : fileType === 'image'
            ? `支持JPG、PNG、JPEG，最多上传${maxCount || '一'}张，单张最大${tip}`
            : fileType === 'excel'
            ? '请上传excel文件，每次上传一个文件'
            : fileType === 'pdf'
            ? '请上传pdf'
            : ''
        }
        {...formItemProps}
      >
        <IUpload {...childProps} onUploadSuccess={handleUploadSuccess} />
      </Form.Item>
      {fileType === 'excel' && parsedResult && (
        <Form.Item label=" " colon={false}>
          {renderParseResult(parsedProps, parsedResult)}
        </Form.Item>
      )}
    </>
  )
}

export default IUploadItem
